import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

class Food {
  element: HTMLElement;
  constructor() {
    // 获取food元素 ！表示必不为空
    this.element = document.getElementById('food')!;
  }

  get X() {
    return this.element.offsetLeft
  }

  get Y() {
    return this.element.offsetTop
  }

  // 修改位置
  change() {
    // [0,290]
    let left = Math.round(Math.random() * 29) * 10
    let top = Math.round(Math.random() * 29) * 10
    this.element.style.left = left + 'px'
    this.element.style.top = top + 'px'
  }
}

// const food = new Food()
// food.change()
class ScorePanel {
  score = 0;
  level = 1;
  scoreEle: HTMLElement;
  levelEle: HTMLElement;
  maxLevel: number;
  //升级所需分
  upScore: number
  // 不传的话默认10
  constructor(maxLevel: number = 10, upScore = 10) {
    this.scoreEle = document.getElementById('score')!;
    this.levelEle = document.getElementById('level')!;
    this.maxLevel = maxLevel
    this.upScore = upScore
  };

  // 加分
  addScore() {
    this.score++
    this.scoreEle.innerHTML = this.score + ''
    if (this.score % this.upScore === 0) {
      this.levelUp()
    }
  }
  levelUp() {
    if (this.level < this.maxLevel) {
      this.levelEle.innerHTML = ++this.level + ''
    }
  }
}

// 第一个参数：最大等级 第二个参数：多少分升一级
// const scorePanel = new ScorePanel(100, 2);
// scorePanel.addScore()

class Snack {
  //蛇头
  head: HTMLElement;
  //蛇身 包括头 实时刷新
  bodies: HTMLCollection;
  //蛇的容器
  element: HTMLElement
  constructor() {
    this.element = document.getElementById('snack')!
    this.head = document.querySelector('#snack>div')!
    // document.querySelectorAll('#snack>div')!
    // 上面的是nodelist结点不能实时刷新
    this.bodies = document.getElementById('snack')!.getElementsByTagName('div')
  }
  // 蛇头坐标
  get X() {
    return this.head.offsetLeft
  }
  get Y() {
    return this.head.offsetTop
  }

  set X(val: number) {
    if (this.X === val) return
    // 0-290
    if (val < 0 || val > 290) {
      throw new Error("斯内克撞墙了!!!");
    }
    // 不能掉头
    if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetLeft === val) {
      // 反方向继续移动
      if (val > this.X) {
        // 右掉头 继续向左
        val = this.X - 10
      } else {
        val = this.X + 10
      }
    }

    this.moveBody()
    this.head.style.left = val + 'px'
    this.checkHeadBody()
  }
  set Y(val: number) {
    if (this.Y === val) return
    if (val < 0 || val > 290) {
      throw new Error("斯内克撞墙了!!!");
    }
    if (this.bodies[1] && (this.bodies[1] as HTMLElement).offsetTop === val) {
      // 反方向继续移动
      if (val > this.Y) {
        val = this.Y - 10
      } else {
        val = this.Y + 10
      }
    }
    this.moveBody()
    this.head.style.top = val + 'px'
    this.checkHeadBody()
  }

  // 蛇变长
  addBody() {
    // 在element里加一个div
    this.element.insertAdjacentHTML('beforeend', '<div></div>')
  }
  moveBody() {
    // 后边的身体在前面的
    for (let i = this.bodies.length - 1; i > 0; i--) {
      // 获取前边身体位置
      let X = (this.bodies[i - 1] as HTMLElement).offsetLeft;
      let Y: number = (this.bodies[i - 1] as HTMLElement).offsetTop;
      (this.bodies[i] as HTMLElement).style.left = X + 'px';
      (this.bodies[i] as HTMLElement).style.top = Y + 'px';
    }
  }
  checkHeadBody() {
    // 获取身体 检查是否和蛇头重叠
    for (let i = 1; i < this.bodies.length; i++) {
      let bd = this.bodies[i] as HTMLElement
      if (this.X === bd.offsetLeft && this.Y === bd.offsetTop) {
        // 头撞到了身体
        throw new Error("斯内克撞到自己了!!!");
      }
    }
  }
}

// 控制所有类
class GameControl {
  snack: Snack;
  food: Food;
  scorePanel: ScorePanel;
  //鼠标点击的方向
  direction: string = ''
  //是否结束
  isLive = true
  constructor() {
    this.snack = new Snack()
    this.food = new Food()
    this.scorePanel = new ScorePanel()

    this.init()
  }
  //游戏初始化 调用后游戏开始
  init() {
    document.addEventListener('keydown', this.keydownHandler.bind(this))
    this.run()
  }
  //键盘按下的函数
  keydownHandler(e: KeyboardEvent) {
    this.direction = e.key
  }
  // 蛇移动
  run() {
    // 蛇坐标
    let X = this.snack.X
    let Y = this.snack.Y
    switch (this.direction) {
      case 'ArrowUp':
        Y -= 10
        break
      case 'ArrowDown':
        Y += 10
        break
      case 'ArrowLeft':
        X -= 10
        break
      case 'ArrowRight':
        X += 10
        break
    }

    //是否吃到食物
    if (this.checkEat(X, Y)) {
      this.food.change()
      this.scorePanel.addScore()
      this.snack.addBody()
    }

    try {
      this.snack.X = X
      this.snack.Y = Y
    } catch (error: any) {
      alert(error.message)
      this.isLive = false
    }
    this.isLive && setTimeout(this.run.bind(this), 300 - (this.scorePanel.level - 1) * 30)
  }

  //是否吃到食物
  checkEat(X: number, Y: number) {
    return X === this.food.X && Y === this.food.Y
  }
}

const gamecontrol = new GameControl()
gamecontrol.init